package zero.mybatisplus.dynamictable.service.impl;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import zero.mybatisplus.dynamictable.annotation.DynamicTable;
import zero.mybatisplus.dynamictable.mapper.DynamicTableMapper;
import zero.mybatisplus.dynamictable.param.DynamicTableParam;
import zero.mybatisplus.dynamictable.service.DynamicTableService;
import zero.mybatisplus.dynamictable.threadlocal.ThreadLocalDynamicTable;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;

public class DynamicTableServiceImpl<M extends DynamicTableMapper<T>, T> extends ServiceImpl<M, T> implements DynamicTableService<T> {
    private final static Logger log = LoggerFactory.getLogger(DynamicTableServiceImpl.class);

    private boolean insertBatchSomeColumnMap(Map<DynamicTableParam, List<T>> handleMap) {
        for (Map.Entry<DynamicTableParam, List<T>> entityList : handleMap.entrySet()) {
            DynamicTableParam key = entityList.getKey();
            List<T> value = entityList.getValue();
            insertBatchSomeColumn(value, key);
        }
        return true;
    }

    private boolean insertBatchSomeColumn(List<T> entityList, DynamicTableParam dynamicTableParam) {
        return super.getBaseMapper().insertBatchSomeColumn(entityList, dynamicTableParam);
    }


    private String getFieldName(Field field) {
        TableField tableField = field.getAnnotation(TableField.class);
        if (!Objects.isNull(tableField)) {
            return tableField.value();
        }
        return field.getName();
    }

    private void handleInsertMap(T t,
                                 Map<DynamicTableParam, List<T>> insertMap,
                                 DynamicTableParam dynamicTableParam,
                                 QueryWrapper<T> qw) {
        //修改
        if (!Objects.isNull(qw) && this.dynamicTableCount(qw, dynamicTableParam) > 0) {
            dynamicTableUpdate(t, qw, dynamicTableParam);
        } else {
            //批量处理
            if (CollectionUtils.isEmpty(insertMap.get(dynamicTableParam))) {
                List<T> list = new ArrayList<>();
                list.add(t);
                insertMap.put(dynamicTableParam, list);
            } else {
                List<T> list = insertMap.get(dynamicTableParam);
                list.add(t);
            }
        }
    }

    @Override
    public boolean saveOrUpdateBatch(List<T> entityList) {
        Assert.isTrue(!CollectionUtils.isEmpty(entityList), "error: entityList must not empty");
        Map<DynamicTableParam, List<T>> insertMap = new HashMap<>();
        Class<?> aClass = entityList.get(0).getClass();
        DynamicTable dynamicTable = aClass.getAnnotation(DynamicTable.class);
        TableName tableName = aClass.getAnnotation(TableName.class);
        int flag = 0;
        for (T t : entityList) {
            //单表
            Field[] declaredFields = aClass.getDeclaredFields();
            DynamicTableParam dynamicTableParam = null;
            QueryWrapper<T> qw = new QueryWrapper<>();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                try {
                    Object o = field.get(t);
                    String fieldName = getFieldName(field);
                    //只处理单个主键,多个主键请改为单个主键,或者自己扩充
                    TableId tableId = field.getAnnotation(TableId.class);
                    if (!Objects.isNull(tableId)) {
                        qw.eq(fieldName, o);
                        flag++;
                    }
                    if (!Objects.isNull(dynamicTable)) {
                        if (Objects.equals(fieldName, dynamicTable.dateField())) {
                            //分表
                            dynamicTableParam = DynamicTableParam.initDateValue(tableName.value(), dynamicTable.type(), o);
                            flag++;
                        }
                    }
                    if (flag >= 2) {
                        break;
                    }
                } catch (IllegalAccessException e) {
                    log.error("字段信息获取异常{}", e.getMessage());
                }
            }
            handleInsertMap(t, insertMap, dynamicTableParam, qw);
        }
        insertBatchSomeColumnMap(insertMap);
        return true;
    }

    @Override
    public boolean saveOrUpdateBatch(List<T> entityList, DynamicTableParam dynamicTableParam) {
        Assert.isTrue(!CollectionUtils.isEmpty(entityList), "error: entityList must not empty");
        Map<DynamicTableParam, List<T>> insertMap = new HashMap<>();
        Field[] declaredFields = entityList.get(0).getClass().getDeclaredFields();
        for (T t : entityList) {
            QueryWrapper<T> qw = new QueryWrapper<>();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                try {
                    Object o = field.get(t);
                    String fieldName = getFieldName(field);
                    //只处理单个主键,多个主键请改为单个主键,或者自己扩充
                    TableId tableId = field.getAnnotation(TableId.class);
                    if (!Objects.isNull(tableId)) {
                        qw.eq(fieldName, o);
                    }
                } catch (IllegalAccessException e) {
                    log.error("字段信息获取异常{}", e.getMessage());
                }
            }
            handleInsertMap(t, insertMap, dynamicTableParam, qw);
        }
        insertBatchSomeColumnMap(insertMap);
        return true;
    }

    @Override
    public boolean saveBatch(List<T> entityList) {
        Assert.isTrue(!CollectionUtils.isEmpty(entityList), "error: entityList must not empty");
        Map<DynamicTableParam, List<T>> insertMap = new HashMap<>();
        Class<?> aClass = entityList.get(0).getClass();
        DynamicTable dynamicTable = aClass.getAnnotation(DynamicTable.class);
        TableName tableName = aClass.getAnnotation(TableName.class);
        for (T t : entityList) {
            //单表
            Field[] declaredFields = aClass.getDeclaredFields();
            DynamicTableParam dynamicTableParam = null;
            for (Field field : declaredFields) {
                field.setAccessible(true);
                try {
                    Object o = field.get(t);
                    if (!Objects.isNull(dynamicTable)) {
                        if (Objects.equals(getFieldName(field), dynamicTable.dateField())) {
                            //分表
                            dynamicTableParam = DynamicTableParam.initDateValue(tableName.value(), dynamicTable.type(), o);
                            break;
                        }
                    }
                } catch (IllegalAccessException e) {
                    log.error("字段信息获取异常{}", e.getMessage());
                }
            }
            handleInsertMap(t, insertMap, dynamicTableParam, null);
        }
        insertBatchSomeColumnMap(insertMap);
        return true;
    }

    @Override
    public boolean saveBatch(List<T> entityList, DynamicTableParam dynamicTableParam) {
        Assert.isTrue(!CollectionUtils.isEmpty(entityList), "error: entityList must not empty");
        Map<DynamicTableParam, List<T>> insertMap = new HashMap<>();
        insertMap.put(dynamicTableParam, entityList);
        insertBatchSomeColumnMap(insertMap);
        return false;
    }

    @Override
    public int dynamicTableCount(QueryWrapper<T> queryWrapper, DynamicTableParam dynamicTableParam) {
        try {
            ThreadLocalDynamicTable.setDynamicTableParam(dynamicTableParam);
            return this.count(queryWrapper);
        } finally {
            ThreadLocalDynamicTable.remove();
        }
    }

    @Override
    public boolean dynamicTableUpdate(T t, QueryWrapper<T> queryWrapper, DynamicTableParam dynamicTableParam) {
        try {
            ThreadLocalDynamicTable.setDynamicTableParam(dynamicTableParam);
            return this.update(t, queryWrapper);
        } finally {
            ThreadLocalDynamicTable.remove();
        }
    }

    @Override
    public boolean delById(Serializable id, DynamicTableParam dynamicTableParam) {
        try {
            ThreadLocalDynamicTable.setDynamicTableParam(dynamicTableParam);
            return this.removeById(id);
        } finally {
            ThreadLocalDynamicTable.remove();
        }
    }

    @Override
    public T getById(Serializable id, DynamicTableParam dynamicTableParam) {
        try {
            ThreadLocalDynamicTable.setDynamicTableParam(dynamicTableParam);
            return this.getById(id);
        } finally {
            ThreadLocalDynamicTable.remove();
        }
    }
}
